home *** CD-ROM | disk | FTP | other *** search
- Frequently Asked Questions (FAQS);faqs.223
-
-
-
- A: In general, you cannot. You must provide a version of that
- other function which accepts a va_list pointer, as does vfprintf
- in the example above. If the arguments must be passed directly
- as actual arguments (not indirectly through a va_list pointer)
- to another function which is itself variadic (for which you do
- not have the option of creating an alternate, va_list-accepting
- version) no portable solution is possible. (The problem can be
- solved by resorting to machine-specific assembly language.)
-
- 7.5: How can I call a function with an argument list built up at run
- time?
-
- A: There is no guaranteed or portable way to do this. If you're
- curious, ask this list's editor, who has a few wacky ideas you
- could try... (See also question 16.10.)
-
-
- Section 8. Boolean Expressions and Variables
-
- 8.1: What is the right type to use for boolean values in C? Why
- isn't it a standard type? Should #defines or enums be used for
- the true and false values?
-
- A: C does not provide a standard boolean type, because picking one
- involves a space/time tradeoff which is best decided by the
- programmer. (Using an int for a boolean may be faster, while
- using char may save data space.)
-
- The choice between #defines and enums is arbitrary and not
- terribly interesting (see also question 9.1). Use any of
-
- #define TRUE 1 #define YES 1
- #define FALSE 0 #define NO 0
-
- enum bool {false, true}; enum bool {no, yes};
-
- or use raw 1 and 0, as long as you are consistent within one
- program or project. (An enum may be preferable if your debugger
- expands enum values when examining variables.)
-
- Some people prefer variants like
-
- #define TRUE (1==1)
- #define FALSE (!TRUE)
-
- or define "helper" macros such as
-
- #define Istrue(e) ((e) != 0)
-
- These don't buy anything (see question 8.2 below; see also
- question 1.6).
-
- 8.2: Isn't #defining TRUE to be 1 dangerous, since any nonzero value
- is considered "true" in C? What if a built-in boolean or
- relational operator "returns" something other than 1?
-
- A: It is true (sic) that any nonzero value is considered true in C,
- but this applies only "on input", i.e. where a boolean value is
- expected. When a boolean value is generated by a built-in
- operator, it is guaranteed to be 1 or 0. Therefore, the test
-
- if((a == b) == TRUE)
-
- will work as expected (as long as TRUE is 1), but it is
- obviously silly. In general, explicit tests against TRUE and
- FALSE are undesirable, because some library functions (notably
- isupper, isalpha, etc.) return, on success, a nonzero value
- which is _not_ necessarily 1. (Besides, if you believe that
- "if((a == b) == TRUE)" is an improvement over "if(a == b)", why
- stop there? Why not use "if(((a == b) == TRUE) == TRUE)"?) A
- good rule of thumb is to use TRUE and FALSE (or the like) only
- for assignment to a Boolean variable, or as the return value
- from a Boolean function, never in a comparison.
-
- The preprocessor macros TRUE and FALSE (and, of course, NULL)
- are used for code readability, not because the underlying values
- might ever change. (See also question 1.7.)
-
- References: K&R I Sec. 2.7 p. 41; K&R II Sec. 2.6 p. 42,
- Sec. A7.4.7 p. 204, Sec. A7.9 p. 206; ANSI Secs. 3.3.3.3, 3.3.8,
- 3.3.9, 3.3.13, 3.3.14, 3.3.15, 3.6.4.1, 3.6.5; Achilles and the
- Tortoise.
-
-
- Section 9. Structs, Enums, and Unions
-
- 9.1: What is the difference between an enum and a series of
- preprocessor #defines?
-
- A: At the present time, there is little difference. Although many
- people might have wished otherwise, the ANSI standard says that
- enumerations may be freely intermixed with integral types,
- without errors. (If such intermixing were disallowed without
- explicit casts, judicious use of enums could catch certain
- programming errors.)
-
- The primary advantages of enums are that the numeric values are
- automatically assigned, and that a debugger may be able to
- display the symbolic values when enum variables are examined.
- (A compiler may also generate nonfatal warnings when enums and
- ints are indiscriminately mixed, since doing so can still be
- considered bad style even though it is not strictly illegal). A
- disadvantage is that the programmer has little control over the
- size (or over those nonfatal warnings).
-
- References: K&R II Sec. 2.3 p. 39, Sec. A4.2 p. 196; H&S
- Sec. 5.5 p. 100; ANSI Secs. 3.1.2.5, 3.5.2, 3.5.2.2 .
-
- 9.2: I heard that structures could be assigned to variables and
- passed to and from functions, but K&R I says not.
-
- A: What K&R I said was that the restrictions on struct operations
- would be lifted in a forthcoming version of the compiler, and in
- fact struct assignment and passing were fully functional in
- Ritchie's compiler even as K&R I was being published. Although
- a few early C compilers lacked struct assignment, all modern
- compilers support it, and it is part of the ANSI C standard, so
- there should be no reluctance to use it.
-
- References: K&R I Sec. 6.2 p. 121; K&R II Sec. 6.2 p. 129; H&S
- Sec. 5.6.2 p. 103; ANSI Secs. 3.1.2.5, 3.2.2.1, 3.3.16 .
-
- 9.3: How does struct passing and returning work?
-
- A: When structures are passed as arguments to functions, the entire
- struct is typically pushed on the stack, using as many words as
- are required. (Programmers often choose to use pointers to
- structures instead, precisely to avoid this overhead.)
-
- Structures are often returned from functions in a location
- pointed to by an extra, compiler-supplied "hidden" argument to
- the function. Some older compilers used a special, static
- location for structure returns, although this made struct-valued
- functions nonreentrant, which ANSI C disallows.
-
- Reference: ANSI Sec. 2.2.3 p. 13.
-
- 9.4: The following program works correctly, but it dumps core after
- it finishes. Why?
-
- struct list
- {
- char *item;
- struct list *next;
- }
-
- /* Here is the main program. */
-
- main(argc, argv)
- ...
-
- A: A missing semicolon causes the compiler to believe that main
- returns a structure. (The connection is hard to see because of
- the intervening comment.) Since struct-valued functions are
- usually implemented by adding a hidden return pointer, the
- generated code for main() tries to accept three arguments,
- although only two are passed (in this case, by the C start-up
- code). See also question 17.15.
-
- Reference: CT&P Sec. 2.3 pp. 21-2.
-
- 9.5: Why can't you compare structs?
-
- A: There is no reasonable way for a compiler to implement struct
- comparison which is consistent with C's low-level flavor. A
- byte-by-byte comparison could be invalidated by random bits
- present in unused "holes" in the structure (such padding is used
- to keep the alignment of later fields correct). A field-by-
- field comparison would require unacceptable amounts of
- repetitive, in-line code for large structures.
-
- If you want to compare two structures, you must write your own
- function to do so. C++ would let you arrange for the ==
- operator to map to your function.
-
- References: K&R II Sec. 6.2 p. 129; H&S Sec. 5.6.2 p. 103; ANSI
- Rationale Sec. 3.3.9 p. 47.
-
- 9.6: I came across some code that declared a structure like this:
-
- struct name
- {
- int namelen;
- char name[1];
- };
-
- and then did some tricky allocation to make the name array act
- like it had several elements. Is this legal and/or portable?
-
- A: This technique is popular, although Dennis Ritchie has called it
- "unwarranted chumminess with the compiler." The ANSI C standard
- allows it only implicitly. It seems to be portable to all known
- implementations. (Compilers which check array bounds carefully
- might issue warnings.)
-
- References: ANSI Rationale Sec. 3.5.4.2 pp. 54-5.
-
- 9.7: How can I determine the byte offset of a field within a
- structure?
-
- A: ANSI C defines the offsetof macro, which should be used if
- available; see <stddef.h>. If you don't have it, a suggested
- implementation is
-
- #define offsetof(type, mem) ((size_t) \
- ((char *)&((type *) 0)->mem - (char *)((type *) 0)))
-
- This implementation is not 100% portable; some compilers may
- legitimately refuse to accept it.
-
- See the next question for a usage hint.
-
- References: ANSI Sec. 4.1.5 , Rationale Sec. 3.5.4.2 p. 55.
-
- 9.8: How can I access structure fields by name at run time?
-
- A: Build a table of names and offsets, using the offsetof() macro.
- The offset of field b in struct a is
-
- offsetb = offsetof(struct a, b)
-
- If structp is a pointer to an instance of this structure, and b
- is an int field with offset as computed above, b's value can be
- set indirectly with
-
- *(int *)((char *)structp + offsetb) = value;
-
- 9.9: Why does sizeof report a larger size than I expect for a
- structure type, as if there was padding at the end?
-
- A: Structures may have this padding (as well as internal padding;
- see also question 9.5), so that alignment properties will be
- preserved when an array of contiguous structures is allocated.
-
- 9.10: My compiler is leaving holes in structures, which is wasting
- space and preventing "binary" I/O to external data files. Can I
- turn off the padding, or otherwise control the alignment of
- structs?
-
- A: Your compiler may provide an extension to give you this control
- (perhaps a #pragma), but there is no standard method. See also
- question 17.2.
-
- 9.11: Can I initialize unions?
-
- A: ANSI Standard C allows an initializer for the first member of a
- union. There is no standard way of initializing the other
- members (nor, under a pre-ANSI compiler, is there generally any
- way of initializing any of them).
-
-
- Section 10. Declarations
-
- 10.1: How do you decide which integer type to use?
-
- A: If you might need large values (above 32767 or below -32767),
- use long. Otherwise, if space is very important (there are
- large arrays or many structures), use short. Otherwise, use
- int. If well-defined overflow characteristics are important
- and/or negative values are not, use the corresponding unsigned
- types. (But beware of mixing signed and unsigned in
- expressions.) Similar arguments apply when deciding between
- float and double.
-
- Although char or unsigned char can be used as a "tiny" int type,
- doing so is often more trouble than it's worth, due to
- unpredictable sign extension and increased code size.
-
- These rules obviously don't apply if the address of a variable
- is taken and must have a particular type.
-
- If for some reason you need to declare something with an _exact_
- size (usually the only good reason for doing so is when
- attempting to conform to some externally-imposed storage layout,
- but see question 17.2), be sure to encapsulate the choice behind
- an appropriate typedef.
-
- 10.2: I can't seem to define a linked list successfully. I tried
-
- typedef struct
- {
- char *item;
- NODEPTR next;
- } *NODEPTR;
-
- but the compiler gave me error messages. Can't a struct in C
- contain a pointer to itself?
-
- A: Structs in C can certainly contain pointers to themselves; the
- discussion and example in section 6.5 of K&R make this clear.
- The problem with this example is that the NODEPTR typedef is not
- complete at the point where the "next" field is declared. To
- fix it, first give the structure a tag ("struct node"). Then,
- declare the "next" field as "struct node *next;", and/or move
- the typedef declaration wholly before or wholly after the struct
- declaration. One fixed version would be
-
- struct node
- {
- char *item;
- struct node *next;
- };
-
- typedef struct node *NODEPTR;
-
- , and there are at least three other equivalently correct ways
- of arranging it.
-
- A similar problem, with a similar solution, can arise when
- attempting to declare a pair of typedef'ed mutually recursive
- structures.
-
- References: K&R I Sec. 6.5 p. 101; K&R II Sec. 6.5 p. 139; H&S
- Sec. 5.6.1 p. 102; ANSI Sec. 3.5.2.3 .
-
- 10.3: How do I declare an array of pointers to functions returning
- pointers to functions returning pointers to characters?
-
- A: This question can be answered in at least three ways (all
- declare the hypothetical array with 5 elements):
-
- 1. char *(*(*a[5])())();
-
- 2. Build the declaration up in stages, using typedefs:
-
- typedef char *pc; /* pointer to char */
- typedef pc fpc(); /* function returning pointer to char */
- typedef fpc *pfpc; /* pointer to above */
- typedef pfpc fpfpc(); /* function returning... */
- typedef fpfpc *pfpfpc; /* pointer to... */
- pfpfpc a[5]; /* array of... */
-
- 3. Use the cdecl program, which turns English into C and vice
- versa:
-
- cdecl> declare a as array 5 of pointer to function returning
- pointer to function returning pointer to char
- char *(*(*a[5])())()
-
- cdecl can also explain complicated declarations, help with
- casts, and indicate which set of parentheses the arguments
- go in (for complicated function definitions, like the
- above). Versions of cdecl are in volume 14 of
- comp.sources.unix (see question 17.8) and K&R II.
-
- Any good book on C should explain how to read these complicated
- C declarations "inside out" to understand them ("declaration
- mimics use").
-
- References: K&R II Sec. 5.12 p. 122; H&S Sec. 5.10.1 p. 116.
-
- 10.4: I'm building a state machine with a bunch of functions, one for
- each state. I want to implement state transitions by having
- each function return a pointer to the next state function. I
- find a limitation in C's declaration mechanism: there's no way
- to declare these functions as returning a pointer to a function
- returning a pointer to a function returning a pointer to a
- function...
-
- A: You can't do it directly. Either have the function return a
- generic function pointer type, and apply a cast before calling
- through it; or have it return a structure containing only a
- pointer to a function returning that structure.
-
- 10.5: What's the best way to declare and define global variables?
-
- A: First, though there can be many _declarations_ (and in many
- translation units) of a single "global" (strictly speaking,
- "external") variable (or function), there must be exactly one
- _definition_. (The definition is the declaration that actually
- allocates space, and provides an initialization value, if any.)
- It is best to place the definition in some central (to the
- program, or to the module) .c file, with an external declaration
- in a header (".h") file, which is #included wherever the
- declaration is needed. The .c file containing the definition
- should also #include the header file containing the external
- declaration, so that the compiler can check that the
- declarations match.
-
- This rule promotes a high degree of portability, and is
- consistent with the requirements of the ANSI C Standard. Note
- that Unix compilers and linkers typically use a "common model"
- which allows multiple (uninitialized) definitions. A few very
- odd systems may require an explicit initializer to distinguish a
- definition from an external declaration.
-
- It is possible to use preprocessor tricks to arrange that the
- declaration need only be typed once, in the header file, and
- "turned into" a definition, during exactly one #inclusion, via a
- special #define.
-
- References: K&R I Sec. 4.5 pp. 76-7; K&R II Sec. 4.4 pp. 80-1;
- ANSI Sec. 3.1.2.2 (esp. Rationale), Secs. 3.7, 3.7.2,
- Sec. F.5.11 .
-
- 10.6: I finally figured out the syntax for declaring pointers to
- functions, but now how do I initialize one?
-
- A: Use something like
-
- extern int func();
- int (*fp)() = func;
-
- When the name of a function appears in an expression but is not
- being called (i.e. is not followed by a "("), it "decays" into a
- pointer (i.e. it has its address implicitly taken), much as an
- array name does.
-
- An explicit extern declaration for the function is normally
- needed, since implicit external function declaration does not
- happen in this case (again, because the function name is not
- followed by a "(").
-
- 10.7: I've seen different methods used for calling through pointers to
- functions. What's the story?
-
- A: Originally, a pointer to a function had to be "turned into" a
- "real" function, with the * operator (and an extra pair of
- parentheses, to keep the precedence straight), before calling:
-
- int r, func(), (*fp)() = func;
- r = (*fp)();
-
- It can also be argued that functions are always called through
- pointers, but that "real" functions decay implicitly into
- pointers (in expressions, as they do in initializations) and so
- cause no trouble. This reasoning, made widespread through pcc
- and adopted in the ANSI standard, means that
-
- r = fp();
-
- is legal and works correctly, whether fp is a function or a
- pointer to one. (The usage has always been unambiguous; there
- is nothing you ever could have done with a function pointer
- followed by an argument list except call through it.) An
- explicit * is harmless, and still allowed (and recommended, if
- portability to older compilers is important).
-
- References: ANSI Sec. 3.3.2.2 p. 41, Rationale p. 41.
-
-
- Section 11. Stdio
-
- 11.1: Why doesn't this code:
-
- char c;
- while((c = getchar()) != EOF)...
-
- work?
-
- A: For one thing, the variable to hold getchar's return value must
- be an int. getchar can return all possible character values, as
- well as EOF. By passing getchar's return value through a char,
- either a normal character might be misinterpreted as EOF, or the
- EOF might be altered and so never seen.
-
- References: CT&P Sec. 5.1 p. 70.
-
- 11.2: Why does errno contain ENOTTY after a call to printf?
-
- A: Many implementations of the stdio package adjust their behavior
- slightly if stdout is a terminal. To make the determination,
- these implementations perform an operation which fails (with
- ENOTTY) if stdout is not a terminal. Although the output
- operation goes on to complete successfully, errno still contains
- ENOTTY.
-
- Reference: CT&P Sec. 5.4 p. 73.
-
- 11.3: My program's prompts and intermediate output don't always show
- up on the screen, especially when I pipe the output through
- another program.
-
- A: It is best to use an explicit fflush(stdout) whenever output
- should definitely be visible. Several mechanisms attempt to
- perform the fflush for you, at the "right time," but they tend
- to apply only when stdout is a terminal. (See question 11.2.)
-
- 11.4: When I read from the keyboard with scanf, it seems to hang until
- I type one extra line of input.
-
- A: scanf was designed for free-format input, which is seldom what
- you want when reading from the keyboard. In particular, "\n" in
- a format string does _not_ mean to expect a newline, but rather
- to read and discard characters as long as each is a whitespace
- character.
-
- It is usually better to use fgets to read a whole line, and
- then use sscanf or other string functions to pick apart the line
- buffer. If you do use sscanf, don't forget to check the return
- value to make sure that the expected number of items were found.
-
- 11.5: How can I flush pending input so that a user's typeahead isn't
- read at the next prompt? Will fflush(stdin) work?
-
- A: fflush is defined only for output streams. Since its definition
- of "flush" is to complete the writing of buffered characters
- (not to discard them), discarding unread input would not be an
- analogous meaning for fflush on input streams. There is no
- standard way to discard unread characters from a stdio input
- buffer, nor would such a way be sufficient; unread characters
- can also accumulate in other, OS-level input buffers.
-
- 11.6: Once I've used freopen, how can I get the original stdout (or
- stdin) back?
-
- A: If you need to switch back and forth, the best all-around
- solution is not to use freopen in the first place. Try using
- your own explicit output (or input) stream variable, which you
- can reassign at will, while leaving the original stdout (or
- stdin) undisturbed.
-
- 11.7: How can I recover the file name given an open file descriptor?
-
- A: This problem is, in general, insoluble. Under Unix, for
- instance, a scan of the entire disk, (perhaps requiring special
- permissions) would theoretically be required, and would fail if
- the file descriptor was a pipe or referred to a deleted file
- (and could give a misleading answer for a file with multiple
- links). It is best to remember the names of files yourself when
- you open them (perhaps with a wrapper function around fopen).
-
-
- Section 12. Library Subroutines
-
- 12.1: I'm trying to sort an array of strings with qsort, using strcmp
- as the comparison function, but it's not working.
-
- A: By "array of strings" you probably mean "array of pointers to
- char." The arguments to qsort's comparison function are
- pointers to the objects being sorted, in this case, pointers to
- pointers to char. (strcmp, of course, accepts simple pointers
- to char.)
-
- The comparison routine's arguments are expressed as "generic
- pointers," void * or char *. They must be converted back to
- what they "really are" (char **) and dereferenced, yielding
- char *'s which can be usefully compared. Write a comparison
- function like this:
-
- int pstrcmp(p1, p2) /* compare strings through pointers */
- char *p1, *p2; /* void * for ANSI C */
- {
- return strcmp(*(char **)p1, *(char **)p2);
- }
-
- 12.2: Now I'm trying to sort an array of structures with qsort. My
- comparison routine takes pointers to structures, but the
- compiler complains that the function is of the wrong type for
- qsort. How can I cast the function pointer to shut off the
- warning?
-
- A: The conversions must be in the comparison function, which must
- be declared as accepting "generic pointers" (void * or char *)
- as discussed above.
-
- 12.3: How can I convert numbers to strings (the opposite of atoi)? Is
- there an itoa function?
-
- A: Just use sprintf. (You'll have to allocate space for the result
- somewhere anyway; see questions 3.1 and 3.2. Don't worry that
- sprintf may be overkill, potentially wasting run time or code
- space; it works well in practice.)
-
- References: K&R I Sec. 3.6 p. 60; K&R II Sec. 3.6 p. 64.
-
- 12.4: How can I get the time of day in a C program?
-
- A: Just use the time, ctime, and/or localtime functions. (These
- routines have been around for years, and are in the ANSI
- standard.) Here is a simple example:
-
- #include <stdio.h>
- #include <time.h>
-
- main()
- {
- time_t now;
- time(&now);
- printf("It's %.24s.\n", ctime(&now));
- exit(0);
- }
-
- References: ANSI Sec. 4.12 .
-
- 12.5: I know that the library routine localtime will convert a time_t
- into a broken-down struct tm, and that ctime will convert a
- time_t to a printable string. How can I perform the inverse
- operations of converting a struct tm or a string into a time_t?
-
- A: ANSI C specifies a library routine, mktime, which converts a
- struct tm to a time_t. Several public-domain versions of this
- routine are available in case your compiler does not support it
- yet.
-
- Converting a string to a time_t is harder, because of the wide
- variety of date and time formats which should be parsed.
- Public-domain routines have been written for performing this
- function (see, for example, the file partime.c, widely
- distributed with the RCS package), but they are less likely to
- become standardized.
-
- References: K&R II Sec. B10 p. 256; H&S Sec. 20.4 p. 361; ANSI
- Sec. 4.12.2.3 .
-
- 12.6: I need a random number generator.
-
- A: The standard C library has one: rand(). The implementation on
- your system may not be perfect, but writing a better one isn't
- necessarily easy, either.
-
- References: ANSI Sec. 4.10.2.1 p. 154; Knuth Vol. 2 Chap. 3
- pp. 1-177.
-
- 12.7: Each time I run my program, I get the same sequence of numbers
- back from rand().
-
- A: You can call srand() to seed the pseudo-random number generator
- with a more random initial value. Popular random initial seeds
- are the time of day, or the elapsed time before the user presses
- a key.
-
- References: ANSI Sec. 4.10.2.2 p. 154.
-
- 12.8: I need a random true/false value, so I'm taking rand() % 2, but
- it's just alternating 0, 1, 0, 1, 0...
-
- A: Poor pseudorandom number generators (such as the ones
- unfortunately supplied with some systems) are not very random in
- the low-order bits. Try using the higher-order bits.
-
- 12.9-13: I'm trying to port this old A: These routines are variously
- program. Why do I get obsolete; you should
- "undefined external" errors instead:
- for:
-
- 12.9: index? A: use strchr.
- 12.10: rindex? A: use strrchr.
- 12.11: bcopy? A: use memmove, after
- interchanging the first and
- second arguments (see also
- question 5.10).
- 12.12: bcmp? A: use memcmp.
- 12.13: bzero? A: use memset, with a second
- argument of 0.
-
- 12.14: How can I execute a command with system() and read its output
- into a program?
-
- A: Unix and some other systems provide a popen() routine, which
- sets up a stdio stream on a pipe connected to the process
- running a command, so that the output can be read (or the input
- supplied).
-
- 12.15: How can I read a directory in a C program?
-
- A: See if you can use the opendir() and readdir() routines, which
- are available on most Unix systems. Implementations also exist
- for MS-DOS, VMS, and other systems. (MS-DOS also has FINDFIRST
- and FINDNEXT routines which do essentially the same thing.)
-
-
-